package com.iteaj.iot.client.modbus;

import com.iteaj.iot.Message;
import com.iteaj.iot.client.IotClientBootstrap;
import com.iteaj.iot.client.modbus.consts.ModbusCoilStatus;
import com.iteaj.iot.client.modbus.message.ModbusTcpBody;
import com.iteaj.iot.client.modbus.message.ModbusTcpHeader;
import com.iteaj.iot.client.modbus.message.ModbusTcpMessage;
import org.springframework.beans.BeanUtils;

/**
 * ModbusTcp协议的报文构建
 */
public class ModbusTcpMessageBuilder {

    /**
     * 构建Modbus读线圈报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始读
     * @param bitNum 读多少位(一个字节8位)
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildRead01Message(Class<T> tClass, int device, int start, int bitNum) {
        return doBuildReadMessage(tClass, ModbusCode.Read01, device, start, bitNum);
    }

    /**
     * 构建Modbus读线圈报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始读
     * @param bitNum 读多少位(一个字节8位)
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildRead02Message(Class<T> tClass, int device, int start, int bitNum) {
        return doBuildReadMessage(tClass, ModbusCode.Read02, device, start, bitNum);
    }

    /**
     * 构建Modbus读保持寄存器报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始读
     * @param num 读几个寄存器
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildRead03Message(Class<T> tClass, int device, int start, int num) {
        return doBuildReadMessage(tClass, ModbusCode.Read03, device, start, num);
    }

    /**
     * 构建Modbus读输入寄存器报文
     * @param tClass
     * @param device 访问的设备 (1-255)
     * @param start 从哪个寄存器开始读 (1-65535)
     * @param num 读几个寄存器(1-2000)
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildRead04Message(Class<T> tClass, int device, int start, int num) {
        return doBuildReadMessage(tClass, ModbusCode.Read04, device, start, num);
    }

    /**
     * 构建Modbus写单个线圈报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始写
     * @param status 写内容
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildWrite05Message(Class<T> tClass, int device, int start, ModbusCoilStatus status) {
        if(status == null) {
            throw new IllegalArgumentException("[status]必填");
        }

        return doBuildWriteMessage(tClass, ModbusCode.Write05, device, start, 0, status.getCode());
    }

    /**
     * 构建Modbus写多个线圈报文(按位计算)
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始写
     * @param bitNum 写几位 1. ON  0. OFF
     * @param status 写内容
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildWrite0FMessage(Class<T> tClass, int device, int start, int bitNum,  byte[] status) {
        if(status == null) {
            throw new IllegalArgumentException("[status]必填");
        }

        if(bitNum > status.length * 8) {
            throw new IllegalStateException("未满足[bitNum]<=[status.length * 8]");
        }

        return doBuildWriteMessage(tClass, ModbusCode.Write0F, device, start, bitNum, status);
    }

    /**
     * 构建Modbus写单个寄存器报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始写
     * @param write 写内容
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildWrite06Message(Class<T> tClass, int device, int start, byte[] write) {
        return doBuildWriteMessage(tClass, ModbusCode.Write06, device, start, 0, write);
    }

    /**
     * 构建Modbus写多个寄存器报文
     * @param tClass
     * @param device 访问的设备
     * @param start 从哪个寄存器开始写
     * @param num 写几个寄存器
     * @param write 写到设备的内容
     * @param <T>
     * @return
     */
    public static <T extends ModbusTcpMessage> T buildWrite10Message(Class<T> tClass, int device, int start, int num, byte[] write) {
        return doBuildWriteMessage(tClass, ModbusCode.Write10, device, start, num, write);
    }

    protected static <T extends ModbusTcpMessage> T doBuildReadMessage(Class<T> tClass, ModbusCode code, int device, int start, int num) {
        try {
            T t = BeanUtils.instantiateClass(tClass.getConstructor(byte[].class), Message.EMPTY);
            ModbusTcpClientComponent byClass = (ModbusTcpClientComponent)IotClientBootstrap.getClientComponentFactory().getByClass(tClass);

            ModbusTcpBody body = ModbusTcpBody.read(code, (short) start, (short)num);
            ModbusTcpHeader header = ModbusTcpHeader.buildRequestHeader((byte) device, byClass.nextId(), (short) body.getLength());

            t.setBody(body);
            t.setHead(header);
            t.setMessage(null);

            return t;
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return null;
    }

    protected static <T extends ModbusTcpMessage> T doBuildWriteMessage(Class<T> tClass, ModbusCode code, int device, int start, int num, byte[] write) {
        T t = null;
        try {
            t = BeanUtils.instantiateClass(tClass.getConstructor(byte[].class), Message.EMPTY);
            t.setMessage(null);
            ModbusTcpClientComponent byClass = (ModbusTcpClientComponent)IotClientBootstrap.getClientComponentFactory().getByClass(tClass);

            ModbusTcpBody body;
            switch (code) {
                case Write05:
                case Write06:
                    body = ModbusTcpBody.writeSingle(code, (short) start, write); break;
                case Write0F:
                    body = ModbusTcpBody.write0F((short) start, (short)num, write); break;
                case Write10:
                    body = ModbusTcpBody.write10((short) start, (short)num, write); break;

                default: throw new IllegalStateException("不支持写功能码["+code+"]");
            }

            ModbusTcpHeader header = ModbusTcpHeader.buildRequestHeader((byte) device, byClass.nextId(), (short) body.getLength());
            t.setHead(header);
            t.setBody(body);
        } catch (NoSuchMethodException e) {
            e.printStackTrace();
        }

        return t;
    }
}
